package com.qdxwx.manage.shiro.realm;

import com.qdxwx.common.utils.MessageUtils;
import com.qdxwx.data.service.manager.ManagerService;
import com.qdxwx.data.service.manager.RoleService;
import com.qdxwx.data.service.menu.MenuService;
import com.qdxwx.manage.config.ShiroConstants;
import com.qdxwx.manage.shiro.service.PasswordService;
import com.qdxwx.manage.support.AsyncManager;
import com.qdxwx.manage.support.factory.AsyncFactory;
import com.qdxwx.manage.shiro.ShiroUtils;
import com.qdxwx.models.manager.Manager;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

import java.time.LocalDateTime;
import java.util.Set;

/**
 * 自定义Realm 处理登录 权限
 * 
 * @author qdxwx
 */
public class UserRealm extends AuthorizingRealm {
    private static final Logger logger = LoggerFactory.getLogger(UserRealm.class);

    @Autowired
    private MenuService menuService;

    @Autowired
    private RoleService roleService;

    @Autowired
    private ManagerService managerService;

    @Autowired
    private PasswordService passwordService;

    @Override
    public boolean supports(AuthenticationToken token) {
        return token instanceof UserLoginToken;
    }

    /**
     * 授权
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection arg0) {
        Manager user = ShiroUtils.getManager();
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        // 管理员拥有所有权限
        if (user.isAdmin()) {
            info.addRole("admin");
            info.addStringPermission("*:*:*");
        } else {
            // 角色列表
            Set<String> roles = roleService.getKeys(user.getManagerId());
            // 功能列表
            Set<String> menus = menuService.getPermsByManagerId(user.getManagerId());
            // 角色加入AuthorizationInfo认证对象
            info.setRoles(roles);
            // 权限加入AuthorizationInfo认证对象
            info.setStringPermissions(menus);
        }
        return info;
    }

    /**
     * 登录认证
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        UserLoginToken upToken = (UserLoginToken) token;
        String username = upToken.getUsername();
        String loginPwd = upToken.getPassword();
        String message;
        // 查询用户信息
        Manager manager = managerService.getBySearchName(username);
        if (manager == null) {
            message = MessageUtils.message("user.not.exists");
            logger.info("用户[" + username + "]登录失败：{}", message);
            AsyncManager.me().execute(AsyncFactory.recordManagerLogin(username, ShiroConstants.LOGIN_FAIL, message));
            throw new UnknownAccountException(message);
        }
        if (manager.getIsDisabled() > 0) {
            message = MessageUtils.message("user.blocked");
            logger.info("用户[" + username + "]登录失败：{}", message);
            AsyncManager.me().execute(AsyncFactory.recordManagerLogin(username, ShiroConstants.LOGIN_FAIL, message));
            throw new LockedAccountException(message);
        }
        if (loginPwd != null && loginPwd.length() > 1) {
            try {
                passwordService.validate(manager, loginPwd);
            } catch (Exception e) {
                throw new AuthenticationException(e.getMessage(), e);
            }
        }
        AsyncManager.me().execute(AsyncFactory.recordManagerLogin(username, ShiroConstants.LOGIN_SUCCESS, MessageUtils.message("user.login.success")));
        manager.setLastLoginIp(ShiroUtils.getIp());
        manager.setLastLoginTime(LocalDateTime.now());
        managerService.update(manager);
        return new SimpleAuthenticationInfo(manager, loginPwd, getName());
    }

    /**
     * 清理缓存权限
     */
    public void clearCachedAuthorizationInfo() {
        this.clearCachedAuthorizationInfo(SecurityUtils.getSubject().getPrincipals());
    }
}
